📦 Strutture Dati in Python

Liste, Tuple, Set e Dizionari: organizzare e gestire i dati

Introduzione: Oltre le variabili semplici

Finora abbiamo lavorato con variabili che contengono un singolo valore (un numero, una stringa, un booleano). Tuttavia, nella programmazione reale spesso dobbiamo gestire collezioni di dati: liste di nomi, insiemi di numeri unici, tabelle di informazioni correlate. Python offre quattro strutture dati fondamentali per organizzare e manipolare dati complessi in modo efficiente.

📘 Le quattro strutture dati principali
  • Liste (list): collezioni ordinate e modificabili che permettono duplicati
  • Tuple (tuple): collezioni ordinate e immutabili che permettono duplicati
  • Set (set): collezioni non ordinate e modificabili che NON permettono duplicati
  • Dizionari (dict): collezioni di coppie chiave-valore, ordinate e modificabili
Struttura Ordinata Modificabile Duplicati Accesso
Lista ✅ Sì ✅ Sì ✅ Sì Per indice
Tupla ✅ Sì ❌ No ✅ Sì Per indice
Set ❌ No ✅ Sì ❌ No Non indicizzato
Dizionario ✅ Sì (dalla 3.7) ✅ Sì ❌ Chiavi uniche Per chiave

1. Liste (List)

📋Liste: la struttura più versatile

Le liste sono la struttura dati più utilizzata in Python. Sono collezioni ordinate e modificabili di elementi. Pensa a una lista come a un contenitore numerato dove ogni elemento ha una posizione precisa (indice).

1.1 Creare liste

# Lista vuota
lista_vuota = []
altra_vuota = list()

# Lista con elementi
frutti = ["mela", "banana", "arancia", "pera"]
numeri = [10, 20, 30, 40, 50]
mista = [1, "ciao", 3.14, True, None]  # Tipi diversi!

# Lista da range
numeri_sequenza = list(range(1, 6))  # [1, 2, 3, 4, 5]

print(frutti)  # ['mela', 'banana', 'arancia', 'pera']
print(len(frutti))  # 4 (numero di elementi)

Visualizzazione di una lista con indici

frutti = ["mela", "banana", "arancia", "pera"]
0
mela
1
banana
2
arancia
3
pera

Gli indici partono da 0!

1.2 Accedere agli elementi

frutti = ["mela", "banana", "arancia", "pera"]

# Accesso per indice (da 0)
primo = frutti[0]      # "mela"
secondo = frutti[1]    # "banana"

# Indici negativi (dall'ultimo elemento)
ultimo = frutti[-1]    # "pera"
penultimo = frutti[-2] # "arancia"

print(f"Primo frutto: {primo}")
print(f"Ultimo frutto: {ultimo}")

# Modificare un elemento
frutti[1] = "kiwi"
print(frutti)  # ['mela', 'kiwi', 'arancia', 'pera']
⚠️ IndexError: list index out of range

Se provi ad accedere a un indice che non esiste, Python solleva un errore:

frutti = ["mela", "banana"]
print(frutti[5])  # ERRORE! L'indice 5 non esiste

La lista ha solo 2 elementi (indici 0 e 1).

1.3 Slicing: selezionare porzioni di lista

Lo slicing permette di estrarre una sottolista usando la sintassi lista[inizio:fine:step].

numeri = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Sintassi: lista[inizio:fine]  (fine escluso!)
primi_tre = numeri[0:3]      # [0, 1, 2]
dal_terzo = numeri[3:]       # [3, 4, 5, 6, 7, 8, 9]
fino_al_quinto = numeri[:5]  # [0, 1, 2, 3, 4]

# Con step
pari = numeri[::2]           # [0, 2, 4, 6, 8]
dispari = numeri[1::2]       # [1, 3, 5, 7, 9]

# Invertire una lista
invertita = numeri[::-1]     # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

# Porzione specifica
centrale = numeri[3:7]       # [3, 4, 5, 6]
🔑 Regola dello slicing

lista[inizio:fine:step]

  • inizio: indice di partenza (incluso). Default: 0
  • fine: indice finale (escluso). Default: fine lista
  • step: incremento. Default: 1

1.4 Metodi delle liste

append(elemento)

Aggiunge un elemento alla fine

lista.append(5)
insert(pos, elem)

Inserisce elemento in posizione

lista.insert(0, "nuovo")
remove(elemento)

Rimuove prima occorrenza

lista.remove("mela")
pop(indice)

Rimuove e restituisce elemento

ultimo = lista.pop()
clear()

Svuota completamente la lista

lista.clear()
index(elemento)

Restituisce indice di elemento

pos = lista.index("banana")
count(elemento)

Conta occorrenze

n = lista.count(5)
sort()

Ordina lista (in place)

lista.sort()
reverse()

Inverte ordine (in place)

lista.reverse()
copy()

Crea copia della lista

nuova = lista.copy()
extend(altra_lista)

Aggiunge elementi da altra lista

lista.extend([1, 2, 3])
Esempio completo: gestione playlist
# Creare playlist
playlist = ["Song A", "Song B", "Song C"]
print(f"Playlist iniziale: {playlist}")

# Aggiungere canzoni
playlist.append("Song D")
print(f"Dopo append: {playlist}")

# Inserire all'inizio
playlist.insert(0, "Intro")
print(f"Con intro: {playlist}")

# Rimuovere una canzone
playlist.remove("Song B")
print(f"Dopo remove: {playlist}")

# Prendere ultima canzone
ultima = playlist.pop()
print(f"Ultima canzone estratta: {ultima}")
print(f"Playlist ora: {playlist}")

# Ordinare alfabeticamente
playlist.sort()
print(f"Ordinata: {playlist}")

# Invertire ordine
playlist.reverse()
print(f"Invertita: {playlist}")

# Cercare una canzone
if "Song A" in playlist:
    posizione = playlist.index("Song A")
    print(f"Song A è alla posizione {posizione}")

# Numero di canzoni
print(f"Totale canzoni: {len(playlist)}")

1.5 Operazioni con liste

# Concatenazione
lista1 = [1, 2, 3]
lista2 = [4, 5, 6]
unione = lista1 + lista2  # [1, 2, 3, 4, 5, 6]

# Ripetizione
ripetuta = [0] * 5  # [0, 0, 0, 0, 0]
multipla = ["ciao"] * 3  # ['ciao', 'ciao', 'ciao']

# Membership (appartenenza)
numeri = [1, 2, 3, 4, 5]
print(3 in numeri)      # True
print(10 in numeri)     # False
print(10 not in numeri) # True

# Iterazione
for numero in numeri:
    print(numero)

# List comprehension (avanzato ma utile!)
quadrati = [x**2 for x in range(1, 6)]  # [1, 4, 9, 16, 25]
pari = [x for x in range(10) if x % 2 == 0]  # [0, 2, 4, 6, 8]
🚨 Attenzione: copia profonda vs superficiale
# Assegnazione semplice: NON copia, ma crea un alias!
lista1 = [1, 2, 3]
lista2 = lista1  # lista2 punta alla STESSA lista!
lista2[0] = 999
print(lista1)  # [999, 2, 3] - anche lista1 è cambiata!

# Copia corretta
lista1 = [1, 2, 3]
lista2 = lista1.copy()  # Oppure: lista2 = lista1[:]
lista2[0] = 999
print(lista1)  # [1, 2, 3] - lista1 non è stata modificata!

2. Tuple (Tuple)

🔒Tuple: liste immutabili

Le tuple sono molto simili alle liste, ma con una differenza cruciale: sono immutabili. Una volta create, non puoi modificarle, aggiungere o rimuovere elementi. Questo le rende più veloci e sicure per dati che non devono cambiare.

2.1 Creare tuple

# Tupla con parentesi tonde
coordinate = (10, 20)
persona = ("Mario", 30, "Roma")
colori = ("rosso", "verde", "blu")

# Tupla senza parentesi (packing)
punto = 5, 10
print(punto)  # (5, 10)

# Tupla con un solo elemento (serve la virgola!)
singola = (5,)     # Tupla
non_tupla = (5)    # NON è una tupla, è solo il numero 5

# Tupla vuota
vuota = ()
altra_vuota = tuple()

# Creare tupla da lista
lista = [1, 2, 3]
tupla_da_lista = tuple(lista)  # (1, 2, 3)
📘 Perché usare le tuple?
  • Performance: Le tuple sono più veloci delle liste
  • Sicurezza: Garantiscono che i dati non vengano modificati accidentalmente
  • Chiavi dizionario: Le tuple (ma non le liste) possono essere usate come chiavi nei dizionari
  • Unpacking: Perfette per restituire valori multipli da funzioni

2.2 Accedere agli elementi

persona = ("Mario", 30, "Roma", "Ingegnere")

# Accesso per indice (come le liste)
nome = persona[0]      # "Mario"
eta = persona[1]       # 30
ultimo = persona[-1]   # "Ingegnere"

# Slicing (come le liste)
info_base = persona[:2]  # ('Mario', 30)

# Unpacking: assegnare ogni elemento a una variabile
nome, eta, citta, lavoro = persona
print(f"{nome} ha {eta} anni e vive a {citta}")

# Unpacking parziale con *
primo, *resto, ultimo = (1, 2, 3, 4, 5)
# primo = 1, resto = [2, 3, 4], ultimo = 5

2.3 Operazioni con tuple

# Concatenazione
t1 = (1, 2, 3)
t2 = (4, 5, 6)
t3 = t1 + t2  # (1, 2, 3, 4, 5, 6)

# Ripetizione
ripetuta = (0,) * 5  # (0, 0, 0, 0, 0)

# Membership
coordinate = (10, 20, 30)
print(20 in coordinate)  # True

# Metodi disponibili (solo 2!)
numeri = (1, 2, 2, 3, 2, 4)
print(numeri.count(2))  # 3 (quante volte appare 2)
print(numeri.index(3))  # 3 (indice del primo 3)

# Iterazione
for valore in coordinate:
    print(valore)
⚠️ Tuple immutabili ma...
# NON puoi modificare una tupla
tupla = (1, 2, 3)
# tupla[0] = 999  # ERRORE!

# MA se la tupla contiene oggetti mutabili...
tupla_con_lista = (1, 2, [3, 4])
tupla_con_lista[2][0] = 999  # OK! Modifichi la lista, non la tupla
print(tupla_con_lista)  # (1, 2, [999, 4])
Esempio: gestione coordinate GPS
# Le coordinate non devono cambiare → usa tuple!
roma = (41.9028, 12.4964, "Roma")
milano = (45.4642, 9.1900, "Milano")
napoli = (40.8518, 14.2681, "Napoli")

citta = [roma, milano, napoli]

for lat, lon, nome in citta:
    print(f"{nome}: Lat {lat}, Lon {lon}")

# Calcolare distanza media dalla prima città
lat_roma, lon_roma, _ = roma  # _ ignora il valore

for lat, lon, nome in citta[1:]:
    distanza_approx = ((lat - lat_roma)**2 + (lon - lon_roma)**2)**0.5
    print(f"Distanza approssimativa da Roma a {nome}: {distanza_approx:.2f}°")

3. Set (Insiemi)

🎯Set: collezioni di elementi unici

I set sono collezioni non ordinate di elementi unici. Non permettono duplicati e non hanno indici. Sono perfetti per operazioni matematiche sugli insiemi (unione, intersezione, differenza) e per eliminare duplicati.

3.1 Creare set

# Set con graffe
numeri = {1, 2, 3, 4, 5}
frutti = {"mela", "banana", "arancia"}

# Set da lista (elimina duplicati automaticamente!)
lista_con_duplicati = [1, 2, 2, 3, 3, 3, 4]
set_unici = set(lista_con_duplicati)
print(set_unici)  # {1, 2, 3, 4}

# Set da stringa (caratteri unici)
caratteri = set("hello")
print(caratteri)  # {'h', 'e', 'l', 'o'}

# Set vuoto (NON usare {}, sono i dizionari!)
vuoto = set()

# Set non ordinato: l'ordine può variare!
print({3, 1, 2})  # Potrebbe stampare {1, 2, 3} o altro ordine
🚨 Attenzione: {} crea un dizionario, non un set!
non_set = {}  # Questo è un DIZIONARIO vuoto!
print(type(non_set))  # <class 'dict'>

set_corretto = set()  # Questo è un SET vuoto
print(type(set_corretto))  # <class 'set'>

3.2 Operazioni con set

numeri = {1, 2, 3, 4, 5}

# Aggiungere elemento
numeri.add(6)
print(numeri)  # {1, 2, 3, 4, 5, 6}

# Aggiungere elemento già presente (non ha effetto)
numeri.add(3)
print(numeri)  # {1, 2, 3, 4, 5, 6} - nessun duplicato!

# Rimuovere elemento
numeri.remove(4)  # Solleva errore se non esiste
numeri.discard(10)  # Non solleva errore se non esiste

# Membership (molto veloce nei set!)
print(3 in numeri)  # True

# Lunghezza
print(len(numeri))

# Svuotare
numeri.clear()

# Iterazione (ordine non garantito!)
colori = {"rosso", "verde", "blu"}
for colore in colori:
    print(colore)

3.3 Operazioni matematiche sugli insiemi

a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# Unione (tutti gli elementi di entrambi)
unione = a | b  # Oppure: a.union(b)
print(unione)  # {1, 2, 3, 4, 5, 6, 7, 8}

# Intersezione (elementi comuni)
intersezione = a & b  # Oppure: a.intersection(b)
print(intersezione)  # {4, 5}

# Differenza (elementi in a ma non in b)
differenza = a - b  # Oppure: a.difference(b)
print(differenza)  # {1, 2, 3}

# Differenza simmetrica (elementi in a o b ma non in entrambi)
diff_simmetrica = a ^ b  # Oppure: a.symmetric_difference(b)
print(diff_simmetrica)  # {1, 2, 3, 6, 7, 8}

# Verifica sottoinsieme
c = {1, 2, 3}
print(c.issubset(a))  # True (c è contenuto in a)
print(a.issuperset(c))  # True (a contiene c)

# Verifica disgiunti
d = {10, 11}
print(a.isdisjoint(d))  # True (nessun elemento comune)

Visualizzazione operazioni insiemi

A = {1, 2, 3, 4, 5}    B = {4, 5, 6, 7, 8}

Unione (A | B): {1, 2, 3, 4, 5, 6, 7, 8}

Intersezione (A & B): {4, 5}

Differenza (A - B): {1, 2, 3}

Differenza simmetrica (A ^ B): {1, 2, 3, 6, 7, 8}

Esempio: analisi studenti corsi
# Studenti iscritti a diversi corsi
python = {"Alice", "Bob", "Carlo", "Diana"}
java = {"Bob", "Elena", "Franco", "Diana"}
javascript = {"Alice", "Elena", "Giorgio"}

# Studenti che seguono sia Python che Java
entrambi = python & java
print(f"Studenti in Python E Java: {entrambi}")

# Studenti che seguono Python o Java (o entrambi)
almeno_uno = python | java
print(f"Studenti in Python O Java: {almeno_uno}")

# Studenti solo in Python (non in Java)
solo_python = python - java
print(f"Solo Python: {solo_python}")

# Studenti che seguono Python o Java ma non entrambi
uno_o_altro = python ^ java
print(f"Python O Java (ma non entrambi): {uno_o_altro}")

# Tutti gli studenti unici
tutti_studenti = python | java | javascript
print(f"\nTotale studenti: {len(tutti_studenti)}")
print(f"Lista: {sorted(tutti_studenti)}")  # sorted() crea lista ordinata

4. Dizionari (Dictionary)

📖Dizionari: coppie chiave-valore

I dizionari sono collezioni di coppie chiave-valore. Permettono di associare a ogni chiave unica un valore. Sono incredibilmente veloci per la ricerca e perfetti per rappresentare dati strutturati (come oggetti JSON).

4.1 Creare dizionari

# Dizionario con graffe
studente = {
    "nome": "Marco",
    "età": 20,
    "corso": "Informatica",
    "media": 27.5
}

# Dizionario vuoto
vuoto = {}
altro_vuoto = dict()

# Creare con dict()
persona = dict(nome="Lucia", età=25, città="Milano")

# Dizionario da liste di tuple
coppie = [("a", 1), ("b", 2), ("c", 3)]
dizionario = dict(coppie)  # {'a': 1, 'b': 2, 'c': 3}

# Le chiavi possono essere di tipi diversi
misto = {
    "nome": "Test",
    42: "numero",
    (1, 2): "tupla come chiave",
    True: "booleano"
}
⚠️ Chiavi dei dizionari

Le chiavi devono essere immutabili:

  • ✅ Possono essere: stringhe, numeri, tuple
  • ❌ NON possono essere: liste, set, altri dizionari
# OK
d = {"nome": "Marco", 123: "valore", (1,2): "tupla"}

# ERRORE!
# d = {[1, 2]: "lista"}  # TypeError!

4.2 Accedere e modificare valori

studente = {
    "nome": "Marco",
    "età": 20,
    "corso": "Informatica"
}

# Accesso con []
nome = studente["nome"]  # "Marco"
eta = studente["età"]    # 20

# Accesso sicuro con get() (non solleva errore se chiave mancante)
corso = studente.get("corso")  # "Informatica"
voto = studente.get("voto", 0)  # 0 (valore di default se chiave mancante)

# Modificare valore
studente["età"] = 21

# Aggiungere nuova coppia chiave-valore
studente["anno"] = 2
studente["voto"] = 28

print(studente)

# Verificare esistenza chiave
if "nome" in studente:
    print(f"Lo studente si chiama {studente['nome']}")

# Numero di coppie
print(f"Numero di informazioni: {len(studente)}")

4.3 Metodi dei dizionari

keys()

Restituisce tutte le chiavi

chiavi = d.keys()
values()

Restituisce tutti i valori

valori = d.values()
items()

Restituisce coppie (chiave, valore)

coppie = d.items()
get(chiave, default)

Ottiene valore in modo sicuro

val = d.get("k", 0)
pop(chiave)

Rimuove e restituisce valore

val = d.pop("chiave")
popitem()

Rimuove ultima coppia inserita

k, v = d.popitem()
update(altro_dict)

Aggiorna con altro dizionario

d.update({"a": 1})
clear()

Svuota il dizionario

d.clear()
copy()

Crea copia del dizionario

nuovo = d.copy()
setdefault(k, default)

Ottiene o imposta valore default

d.setdefault("k", 0)

4.4 Iterare dizionari

voti = {
    "matematica": 28,
    "fisica": 25,
    "programmazione": 30,
    "inglese": 27
}

# Iterare sulle chiavi
print("Materie:")
for materia in voti:  # Oppure: for materia in voti.keys()
    print(f"  - {materia}")

# Iterare sui valori
print("\nVoti:")
for voto in voti.values():
    print(f"  {voto}")

# Iterare su coppie chiave-valore (più comune!)
print("\nVoti per materia:")
for materia, voto in voti.items():
    print(f"  {materia}: {voto}")

# Dizionario comprehension
voti_quadrati = {materia: voto**2 for materia, voto in voti.items()}
voti_alti = {m: v for m, v in voti.items() if v >= 27}
Esempio: rubrica telefonica
rubrica = {
    "Mario Rossi": "333-1234567",
    "Laura Bianchi": "347-9876543",
    "Giovanni Verdi": "320-5551234"
}

print("=== RUBRICA TELEFONICA ===\n")

# Aggiungere contatto
rubrica["Anna Neri"] = "349-1112222"

# Cercare numero
nome = "Mario Rossi"
if nome in rubrica:
    print(f"Numero di {nome}: {rubrica[nome]}")

# Modificare numero
rubrica["Laura Bianchi"] = "347-0000000"

# Visualizzare tutta la rubrica
print("\nContatti:")
for nome, numero in sorted(rubrica.items()):  # Ordine alfabetico
    print(f"  {nome}: {numero}")

# Contare contatti
print(f"\nTotale contatti: {len(rubrica)}")

# Rimuovere contatto
del rubrica["Giovanni Verdi"]
# Oppure: numero_rimosso = rubrica.pop("Giovanni Verdi")

# Cercare in modo sicuro
numero = rubrica.get("Paolo Gialli", "Non trovato")
print(f"\nNumero Paolo: {numero}")

4.5 Dizionari annidati

# Dizionario di dizionari
studenti = {
    "S001": {
        "nome": "Marco",
        "età": 20,
        "voti": [28, 30, 27]
    },
    "S002": {
        "nome": "Laura",
        "età": 21,
        "voti": [30, 29, 30]
    },
    "S003": {
        "nome": "Giovanni",
        "età": 19,
        "voti": [25, 26, 27]
    }
}

# Accedere a dati annidati
print(studenti["S001"]["nome"])  # "Marco"
print(studenti["S002"]["voti"][0])  # 30

# Iterare dizionario annidato
for matricola, info in studenti.items():
    nome = info["nome"]
    media = sum(info["voti"]) / len(info["voti"])
    print(f"{nome} (Matricola: {matricola})")
    print(f"  Media voti: {media:.1f}")
    print()

5. Quale struttura usare?

Usa LISTE quando:

  • Hai una collezione ordinata
  • Devi modificare elementi
  • Permetti duplicati
  • Accesso per posizione/indice

Esempio: playlist, cronologia, punteggi partite

Usa TUPLE quando:

  • Dati che non devono cambiare
  • Restituire valori multipli
  • Chiavi di dizionari
  • Performance importante

Esempio: coordinate, date, configurazioni

Usa SET quando:

  • Elementi devono essere unici
  • Ordine non importante
  • Operazioni insiemistiche
  • Membership test veloce

Esempio: tag, permessi utente, ID univoci

Usa DIZIONARI quando:

  • Associazione chiave-valore
  • Ricerca veloce per chiave
  • Dati strutturati
  • Contatori o raggruppamenti

Esempio: configurazioni, contatti, cache, JSON

6. Programmi completi di esempio

Gestione inventario negozio
inventario = {
    "P001": {"nome": "Laptop", "prezzo": 899.99, "quantità": 15},
    "P002": {"nome": "Mouse", "prezzo": 19.99, "quantità": 50},
    "P003": {"nome": "Tastiera", "prezzo": 49.99, "quantità": 30},
    "P004": {"nome": "Monitor", "prezzo": 199.99, "quantità": 20}
}

print("=== GESTIONE INVENTARIO ===\n")

# Visualizzare inventario
print("PRODOTTI DISPONIBILI:")
for codice, info in inventario.items():
    print(f"{codice}: {info['nome']}")
    print(f"  Prezzo: €{info['prezzo']:.2f}")
    print(f"  Quantità: {info['quantità']}")
    print()

# Calcolare valore totale inventario
valore_totale = sum(p['prezzo'] * p['quantità'] for p in inventario.values())
print(f"Valore totale inventario: €{valore_totale:.2f}\n")

# Vendere prodotto
codice_venduto = "P002"
quantità_venduta = 5

if codice_venduto in inventario:
    prodotto = inventario[codice_venduto]
    
    if prodotto['quantità'] >= quantità_venduta:
        prodotto['quantità'] -= quantità_venduta
        totale = prodotto['prezzo'] * quantità_venduta
        print(f"✅ Venduti {quantità_venduta} x {prodotto['nome']}")
        print(f"   Totale: €{totale:.2f}")
        print(f"   Rimanenti: {prodotto['quantità']}")
    else:
        print(f"❌ Quantità insufficiente!")
else:
    print(f"❌ Prodotto non trovato!")

print()

# Prodotti in esaurimento
print("⚠️ PRODOTTI IN ESAURIMENTO (< 20):")
for codice, info in inventario.items():
    if info['quantità'] < 20:
        print(f"  {info['nome']}: {info['quantità']} pezzi")

# Aggiungere nuovo prodotto
nuovo_codice = "P005"
inventario[nuovo_codice] = {
    "nome": "Webcam",
    "prezzo": 79.99,
    "quantità": 25
}
print(f"\n✅ Aggiunto nuovo prodotto: {inventario[nuovo_codice]['nome']}")
Analizzatore frequenza parole
def analizza_testo(testo):
    """Analizza frequenza parole in un testo"""
    
    # Pulire e normalizzare testo
    testo = testo.lower()
    
    # Rimuovere punteggiatura
    for carattere in ".,!?;:":
        testo = testo.replace(carattere, "")
    
    # Dividere in parole
    parole = testo.split()
    
    # Contare frequenze con dizionario
    frequenze = {}
    for parola in parole:
        if parola in frequenze:
            frequenze[parola] += 1
        else:
            frequenze[parola] = 1
    
    # Oppure usa il metodo get:
    # frequenze[parola] = frequenze.get(parola, 0) + 1
    
    return frequenze

# Testo di esempio
testo = """
Python è un linguaggio di programmazione fantastico.
Python è facile da imparare e Python è molto potente.
Con Python puoi fare molte cose interessanti.
"""

print("=== ANALISI TESTO ===\n")
print(f"Testo:\n{testo}\n")

frequenze = analizza_testo(testo)

# Numero totale di parole
totale_parole = sum(frequenze.values())
print(f"Parole totali: {totale_parole}")
print(f"Parole uniche: {len(frequenze)}\n")

# Parole più frequenti
print("FREQUENZA PAROLE:")
for parola, frequenza in sorted(frequenze.items(), key=lambda x: x[1], reverse=True):
    print(f"  {parola}: {frequenza}")

print("\n" + "="*40)

# Parole che appaiono più di 2 volte
parole_frequenti = {p: f for p, f in frequenze.items() if f > 2}
print(f"\nParole che appaiono più di 2 volte: {parole_frequenti}")
🎯 Quiz di Autovalutazione

1. Qual è la principale differenza tra liste e tuple?

  • a) Le liste sono più veloci
  • b) Le tuple sono immutabili ✅
  • c) Le liste non permettono duplicati
  • d) Le tuple non sono ordinate

2. Quale struttura dati NON permette duplicati?

  • a) Lista
  • b) Tupla
  • c) Set ✅
  • d) Dizionario

3. Come accedi al valore di una chiave "nome" in un dizionario?

  • a) dict[0]
  • b) dict["nome"]
  • c) dict.nome
  • d) dict(nome)

4. Cosa restituisce [1, 2, 3][1:3]?

  • a) [1, 2]
  • b) [2, 3]
  • c) [1, 2, 3]
  • d) [2]

5. Quale operatore restituisce l'intersezione tra due set?

  • a) |
  • b) &
  • c) -
  • d) ^

6. Cosa succede con lista = [1, 2]; copia = lista; copia[0] = 99?

  • a) Solo copia cambia
  • b) Solo lista cambia
  • c) Entrambe cambiano ✅
  • d) Errore

7. Esercizi pratici

🎓 Esercizi per consolidare le conoscenze

Esercizio 1 - Liste: Scrivi un programma che chieda 5 numeri all'utente, li memorizzi in una lista e calcoli la media, il minimo e il massimo.

Esercizio 2 - Tuple: Crea una tupla di coordinate (x, y, z) e scrivi un programma che calcoli la distanza dall'origine (0, 0, 0).

Esercizio 3 - Set: Date due liste di numeri, trova gli elementi comuni e gli elementi presenti solo nella prima lista usando i set.

Esercizio 4 - Dizionari: Crea un dizionario che rappresenti uno studente con nome, età, corso e lista di voti. Calcola la media dei voti.

Esercizio 5 - Medio: Scrivi un programma che conti quante volte appare ogni carattere in una stringa usando un dizionario.

Esercizio 6 - Medio: Crea una lista di dizionari che rappresentino prodotti (nome, prezzo, quantità). Calcola il valore totale dell'inventario.

Esercizio 7 - Avanzato: Implementa un sistema di voto: permetti agli utenti di votare candidati (senza duplicati) e mostra i risultati finali ordinati per numero di voti.

Esercizio 8 - Sfida: Scrivi un programma che trovi tutti gli anagrammi in una lista di parole usando dizionari e set (due parole sono anagrammi se hanno le stesse lettere in ordine diverso).

8. Best Practices

✅ Consigli per usare le strutture dati efficacemente:
  • Scegli la struttura giusta: Non usare sempre liste per tutto!
  • Usa tuple per dati immutabili: Coordinate, configurazioni, chiavi
  • Set per membership test: Verifica x in set è molto più veloce di x in lista
  • Dizionari per lookup: Cercare per chiave è velocissimo
  • List comprehension per creare liste: Più leggibile e veloce
  • get() per dizionari: Evita errori con chiavi mancanti
  • Non modificare durante iterazione: Crea prima una copia
  • Usa copy() per copie reali: Evita modifiche inaspettate
📚 Prossimi passi:

Ora che conosci le strutture dati, sei pronto per:

  • Funzioni personalizzate e scope delle variabili
  • Lavorare con file (lettura/scrittura)
  • Moduli e import di librerie
  • Gestione degli errori (try-except)
  • Programmazione orientata agli oggetti